package com.nconverter;

import android.media.MediaMetadataRetriever;
import android.os.Looper;

import com.dstukalov.videoconverter.BadMediaException;
import com.dstukalov.videoconverter.MediaConversionException;
import com.dstukalov.videoconverter.MediaConverter;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

//CLASSPATH=/data/local/tmp/nconverter-debug.apk   app_process  /data/local/tmp/ com.nconverter.NConverter /sdcard/DCIM/Camera/VID_20201207_142940.mp4  /sdcard/1.mp4

public class NConverter {

    private static final String TAG = "NConverter";

    private static class ConversionParameters {
        final int mVideoResolution;
        @MediaConverter.VideoCodec
        String mVideoCodec;
        int mVideoBitrate;
        int mAudioBitrate;

        ConversionParameters(final int videoResolution, final @MediaConverter.VideoCodec String videoCodec, final int videoBitrate, final int audioBitrate) {
            mVideoResolution = videoResolution;
            mVideoCodec = videoCodec;
            mVideoBitrate = videoBitrate;
            mAudioBitrate = audioBitrate;
        }

        @Override
        public String toString() {
            return "ConversionParameters{" +
                    "mVideoResolution=" + mVideoResolution +
                    ", mVideoCodec='" + mVideoCodec + '\'' +
                    ", mVideoBitrate=" + mVideoBitrate +
                    ", mAudioBitrate=" + mAudioBitrate +
                    '}';
        }
    }

    private static class ConversionTask extends Thread {

        final MediaConverter mConverter;


        ConversionTask(final File input, final File output, final long timeFrom, final long timeTo, final ConversionParameters conversionParameters) throws FileNotFoundException {

            mConverter = new MediaConverter();
            mConverter.setInput(input);
            if (input.length() > 1024L * 1024 * 1024 * 3) {
                mConverter.setOutput(new FileOutputStream(output));
            } else {
                mConverter.setOutput(output);
            }
            mConverter.setTimeRange(timeFrom, timeTo);
            mConverter.setVideoResolution(conversionParameters.mVideoResolution);
            mConverter.setVideoCodec(conversionParameters.mVideoCodec);
            mConverter.setVideoBitrate(conversionParameters.mVideoBitrate);
            mConverter.setAudioBitrate(conversionParameters.mAudioBitrate);

            mConverter.setListener(percent -> {
                MyLog.i(TAG, "percent" + percent);
                return false;
            });
        }

        @Override
        public void run() {
            long begin = System.currentTimeMillis();
            MyLog.i(TAG, "begin time " + begin);
            try {
                mConverter.convert();
            } catch (BadMediaException | IOException | MediaConversionException e) {
                MyLog.e(TAG, "failed to convert: " + e.toString(), e);
                return;
            }

            MyLog.i(TAG, "Conversion finished, output file size is " + mOutputFile.length());
            MyLog.i(TAG, "cost time=" + (System.currentTimeMillis() - begin) / 1000);
            System.exit(0);
        }
    }

    private static final ConversionParameters CONV_PARAMS_720P = new ConversionParameters(720, MediaConverter.VIDEO_CODEC_H264, 4000000, 192000);
    private static final ConversionParameters CONV_PARAMS_1080P = new ConversionParameters(1080, MediaConverter.VIDEO_CODEC_H264, 6000000, 192000);
    private static final ConversionParameters CONV_PARAMS_720P_H265 = new ConversionParameters(720, MediaConverter.VIDEO_CODEC_H265, 2000000, 192000);
    private static final ConversionParameters CONV_PARAMS_1080P_H265 = new ConversionParameters(1080, MediaConverter.VIDEO_CODEC_H265, 4000000, 192000);
    private static final ConversionParameters CONV_PARAMS_2160P_H265 = new ConversionParameters(2160, MediaConverter.VIDEO_CODEC_H265, 8000000, 192000);

    private static File mOutputFile;

    private static void convert(File input, File output, ConversionParameters parm) {
        mOutputFile = output;
        final MediaMetadataRetriever mediaMetadataRetriever = new MediaMetadataRetriever();
        try {
            mediaMetadataRetriever.setDataSource(input.getAbsolutePath());
        } catch (Exception ex) {
            mediaMetadataRetriever.release();
            MyLog.e(TAG, "MediaMetadataRetriever failed", ex);
            return;
        }

        try {
            long duration = Long.parseLong(mediaMetadataRetriever.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION));
            ConversionTask mConversionTask = new ConversionTask(input, mOutputFile, 0, duration, parm);
            mConversionTask.start();
        } catch (FileNotFoundException ignored) {
        }
        mediaMetadataRetriever.release();
    }


    /*
     *    [ld/le/df] [1.mp4] [2.mp4] [720/1080/2160] [avc/hevc] [vbitrate] [abitreate]
     * */
    public static void main(String[] args) {
        if (BuildConfig.DEBUG) {
            for (String f : args) {
                MyLog.i(TAG, f);
            }
        }
        if (args.length == 0) {
            return;
        }

        switch (args[0]) {
            case "ld":
                Helper.listDecoders();
                return;
            case "le":
                Helper.listEncoders();
                return;
            case "df":
                Helper.dumpinput(args[1]);
                return;
        }

        ConversionParameters parm;
        switch (args[3]) {
            case "2160":
                if (args[4].equalsIgnoreCase("avc")) {
                    MyLog.i(TAG, "not support avc 2160p format");
                    return;
                } else {
                    parm = CONV_PARAMS_2160P_H265;
                }
                break;
            case "720":
                if (args[4].equalsIgnoreCase("avc")) {
                    parm = CONV_PARAMS_720P;
                } else {
                    parm = CONV_PARAMS_720P_H265;
                }
                break;
            case "1080":
                if (args[4].equalsIgnoreCase("avc")) {
                    parm = CONV_PARAMS_1080P;
                } else {
                    parm = CONV_PARAMS_1080P_H265;
                }
                break;
            default:
                parm = CONV_PARAMS_1080P_H265;
        }

        if (args[4].equalsIgnoreCase("avc")) {
            parm.mVideoCodec = MediaConverter.VIDEO_CODEC_H264;
        } else {
            parm.mVideoCodec = MediaConverter.VIDEO_CODEC_H265;
        }

        if (!args[5].isEmpty()) {  //vbitrate
            parm.mVideoBitrate = Integer.parseInt(args[5]);
        }

        if (!args[6].isEmpty()) {
            parm.mAudioBitrate = Integer.parseInt(args[6]);
        }
        File targetfile = new File(args[2]);
        if (targetfile.exists()) {
            System.out.println(String.format("File '%s' already exists. Overwrite? [y/N]", args[2]));
            try {
                int in = System.in.read();
                if (in == 'y' || in == 'Y') {
                    targetfile.delete();
                } else {
                    return;
                }
            } catch (IOException e) {
                e.printStackTrace();
                return;
            }
        }
        Looper.prepareMainLooper();
        MyLog.i(TAG, String.format("%s->%s", args[1], args[2]));
        MyLog.i(TAG, parm.toString());
        convert(new File(args[1]), targetfile, parm);
        Looper.loop();
    }
}
